package com.morningstar.flow.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.morningstar.common.arco.SelectOption;
import com.morningstar.common.dao.entity.CommonQuery;
import com.morningstar.common.result.Result;
import com.morningstar.common.result.ServiceCheckResult;
import com.morningstar.flow.controller.request.ConditionTest;
import com.morningstar.flow.dao.entity.FlowNode;
import com.morningstar.flow.dao.entity.FlowProcess;
import com.morningstar.flow.dao.entity.FlowProcessGroup;
import com.morningstar.flow.flowable.condition.ConditionGroupHandler;
import com.morningstar.flow.service.FlowNodeService;
import com.morningstar.flow.service.FlowProcessGroupService;
import com.morningstar.flow.service.FlowProcessService;
import com.morningstar.system.api.feign.ISystemFormFeign;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.DeploymentBuilder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.morningstar.common.constants.permission.PermissionConstants.*;
import static com.morningstar.flow.constants.FlowStatus.*;

@Slf4j
@RestController
@RequestMapping("/flow/process")
public class FlowProcessController {

    private static final String PERMISSION = "flow.process";
    private static final String PERMISSION_LIST = PERMISSION + LIST;
    private static final String PERMISSION_DETAIL = PERMISSION + DETAIL;
    private static final String PERMISSION_UPDATE = PERMISSION + UPDATE;
    private static final String PERMISSION_ADD = PERMISSION + ADD;
    private static final String PERMISSION_DELETE = PERMISSION + DELETE;
    private static final String PERMISSION_DEPLOY = PERMISSION + ".deploy";
    private static final String PERMISSION_UPDATE_STATUS = PERMISSION + ".update.status";
    private static final String PERMISSION_LIST_GROUP = PERMISSION + ".list.group";

    @Resource
    private FlowProcessService processService;

    @Resource
    private FlowProcessGroupService processGroupService;

    @Resource
    private FlowNodeService flowNodeService;

    @Resource
    private ISystemFormFeign systemFormFeign;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private ConditionGroupHandler conditionGroupHandler;

    /**
     * 通过XML 文件部署流程
     *
     * @param file xml 文件
     * @return 基本结果类
     */
    @PostMapping("/xml/deploy")
    @SaCheckPermission(PERMISSION_DEPLOY)
    public Result<Object> deploy(MultipartFile file) {
        DeploymentBuilder deploymentBuilder;
        try {
            deploymentBuilder = repositoryService.createDeployment()
                    .category("工作流")
                    .name("javaboy的工作流名称")
                    .addInputStream(file.getOriginalFilename(), file.getInputStream())
                    .key("leave");
        } catch (IOException e) {
            log.error(ExceptionUtils.getStackTrace(e));
            return Result.fail("部署失败");
        }
        Deployment deployment = deploymentBuilder.deploy();
        return Result.successData(deployment.getId());
    }

    /**
     * 通过流程设计器部署流程
     *
     * @param flowProcess 流程设计器
     * @return 基本结果类
     */
    @PostMapping("/deploy")
    @SaCheckPermission(PERMISSION_DEPLOY)
    public Result<Object> deploy(@RequestBody FlowProcess flowProcess) {
        processService.deploy(flowProcess);
        return Result.successNoData();
    }

    /**
     * 根据流程组分组
     * 发起流程列表
     *
     * @return 基本结果类
     */
    @PostMapping(value = "/listGroup")
    @SaCheckPermission(PERMISSION_LIST_GROUP)
    public Result<List<FlowProcessGroup>> listGroup() {
        LambdaQueryWrapper<FlowProcessGroup> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByAsc(FlowProcessGroup::getSort);
        queryWrapper.orderByDesc(FlowProcessGroup::getCreateTime);
        List<FlowProcessGroup> groupList = processGroupService.list(queryWrapper);
        LambdaQueryWrapper<FlowProcess> processQueryWrapper = new LambdaQueryWrapper<>();
        processQueryWrapper.in(FlowProcess::getStatus, PROCESS_STATUS_TEST, PROCESS_STATUS_ON_LINE);
        List<FlowProcess> processes = processService.list(processQueryWrapper);
        Map<Long, List<FlowProcess>> processMap = processes.stream().collect(Collectors.groupingBy(FlowProcess::getProcessGroupId));
        groupList.forEach(group -> {
            group.setProcesses(processMap.get(group.getProcessGroupId()));
        });
        return Result.successData(groupList);
    }

    /**
     * 流程列表
     *
     * @param commonQuery 通用查询类
     * @return 列表
     * @throws Exception 异常
     */
    @PostMapping(value = "/list")
    @SaCheckPermission(PERMISSION_LIST)
    public Result<IPage<FlowProcess>> list(@RequestBody CommonQuery<FlowProcess, FlowProcess> commonQuery) throws Exception {
        FlowProcess query = commonQuery.getQuery();
        LambdaQueryWrapper<FlowProcess> queryWrapper = new LambdaQueryWrapper<>();
        if (query != null) {
            queryWrapper.like(StringUtils.isNotBlank(query.getProcessName()), FlowProcess::getProcessName, query.getProcessName());
            queryWrapper.like(StringUtils.isNotBlank(query.getProcessKey()), FlowProcess::getProcessKey, query.getProcessKey());
        }
        queryWrapper.orderByDesc(FlowProcess::getCreateTime);
        Map<Long, String> formMap = systemFormFeign.select().stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getLabel));
        Map<Long, String> groupMap = processGroupService.list().stream().collect(Collectors.toMap(FlowProcessGroup::getProcessGroupId, FlowProcessGroup::getProcessGroupName));
        IPage<FlowProcess> result = processService.page(commonQuery.getPage(), queryWrapper);
        result.getRecords().forEach(item -> {
            item.setFormName(formMap.get(item.getFormId()));
            item.setProcessGroupName(groupMap.get(item.getProcessGroupId()));
        });
        return Result.successData(result);
    }

    /**
     * 新建流程
     *
     * @param flowProcess 流程信息
     * @return 基本结果类
     */
    @PostMapping
    @SaCheckPermission(PERMISSION_ADD)
    public Result<Object> add(@RequestBody FlowProcess flowProcess) {
        ServiceCheckResult checkResult = processService.checkAddAndUpdate(flowProcess);
        if (checkResult.isResult()) {
            return Result.fail(checkResult.getMessage());
        }
        flowProcess.setDeploy(PROCESS_DEPLOY_UN);
        flowProcess.setStatus(PROCESS_STATUS_UN_DEPLOY);
        processService.save(flowProcess);
        return Result.successNoData();
    }

    /**
     * 更新流程基本信息
     *
     * @param flowProcess 流程信息
     * @return 基本结果类
     */
    @PostMapping("/update")
    @SaCheckPermission(PERMISSION_UPDATE)
    public Result<Object> update(@RequestBody FlowProcess flowProcess) {
        ServiceCheckResult checkResult = processService.checkAddAndUpdate(flowProcess);
        if (checkResult.isResult()) {
            return Result.fail(checkResult.getMessage());
        }
        processService.updateById(flowProcess);
        return Result.successNoData();
    }

    /**
     * 删除流程
     *
     * @param processId 流程信息
     * @return 基本结果类
     */
    @GetMapping("/delete")
    @SaCheckPermission(PERMISSION_DELETE)
    public Result<Object> delete(@RequestParam("processId") Long processId) {
        //todo 已部署不可删除
        processService.removeById(processId);
        return Result.successNoData();
    }

    /**
     * 流程详细信息
     *
     * @param processId 流程Id
     * @return 基本结果类
     */
    @GetMapping("/detail")
    @SaCheckPermission(PERMISSION_DETAIL)
    public Result<FlowProcess> detail(@RequestParam("processId") Long processId) {
        FlowProcess process = processService.getById(processId);
        List<FlowNode> nodes = flowNodeService.selectByProcessId(processId);
        process.setNodeList(nodes);
        return Result.successData(process);
    }

    /**
     * 更新流程状态
     *
     * @param flowProcess 流程Id
     * @return 流程状态
     */
    @PostMapping("/updateStatus")
    @SaCheckPermission(PERMISSION_UPDATE_STATUS)
    public Result<Object> updateStatus(@RequestBody FlowProcess flowProcess) {
        Long processId = flowProcess.getProcessId();
        // 更新为测试
        LambdaUpdateWrapper<FlowProcess> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(FlowProcess::getProcessId, processId);
        if (PROCESS_STATUS_TEST.equals(flowProcess.getStatus())) {
            updateWrapper.eq(FlowProcess::getStatus, PROCESS_STATUS_DEPLOY);
            updateWrapper.set(FlowProcess::getStatus, PROCESS_STATUS_TEST);
            boolean result = processService.update(updateWrapper);
            return result ? Result.successNoData() : Result.fail("更新失败,请刷新页面");
        }
        // 更新为上线
        if (PROCESS_STATUS_ON_LINE.equals(flowProcess.getStatus())) {
            updateWrapper.eq(FlowProcess::getStatus, PROCESS_STATUS_TEST);
            updateWrapper.set(FlowProcess::getStatus, PROCESS_STATUS_ON_LINE);
            boolean result = processService.update(updateWrapper);
            return result ? Result.successNoData() : Result.fail("更新失败,请刷新页面");
        }
        // 更新为下线
        if (PROCESS_STATUS_OFF_LINE.equals(flowProcess.getStatus())) {
            updateWrapper.eq(FlowProcess::getStatus, PROCESS_STATUS_ON_LINE);
            updateWrapper.set(FlowProcess::getStatus, PROCESS_STATUS_OFF_LINE);
            boolean result = processService.update(updateWrapper);
            return result ? Result.successNoData() : Result.fail("更新失败,请刷新页面");
        }
        return Result.fail("状态错误");
    }

    /**
     * 测试条件
     *
     * @return 基本结果类
     */
    @PostMapping("/conditionTest")
    @SaCheckPermission(PERMISSION_DEPLOY)
    public Result<Boolean> conditionTest(@RequestBody ConditionTest conditionTest) {
        boolean result = conditionGroupHandler.handle(conditionTest.getConditionGroup(), conditionTest.getFormModel());
        return Result.successData(result);
    }

}
